define(function (require, exports, module) {
	require("iscroll");
	/**
	 * @class Scroll
	 * @memberof Nuui
	 * @classdesc 基于iscroll实现的列表组件<br/>
	 * 		实现了下拉刷新,上拉加载更多<br/>
	 * 		因为涉及到高度计算,该组件初始化时需要在onShow方法中进行<br/>
	 * 		以下列举4种场景<br/>
	 * 		1.对现有元素进行渲染,不需要上拉下拉加载,此时不需要送refreshData,loadMoreData,dataRenderer,enablePullDown,enablePullUp参数<br/>
	 * 		2.对数据进行渲染,需要上拉下拉加载,且数据直接展示第一页,此时传入refreshData,loadMoreData,dataRenderer,enablePullDown,enablePullUp,并调用triggerRefresh方法<br/>
	 * 		3.对数据进行渲染,需要上拉下拉加载,且暂不显示任何东西,等待使用者触发,此时传入上述5参数,并手工隐藏容器,然后在调用triggerRefresh前show容器<br/>
	 * 		4.对数据进行渲染,需要上拉加载,不需要下拉刷新,且数据直接展示第一页,此时传入refreshData,loadMoreData,dataRenderer,将enablePullUp设置为true,手工调用refresh
	 * 		由于iscroll本身只有topOffset参数,这里修改源代码增加了bottomOffset参数,如果使用新版本的iscroll需要做相应的源代码修改
	 * @param {element} el - 要渲染的元素,类型为原生的domElement,这个元素里面需要包含两层子元素,如<br/>
	 * 		&ltiv id="scroller">&ltdiv>&ltul>&lt/ul>&lt/div>&lt/div><br/>
	 * 		后续添加的子节点将添加在最里面的子元素里,需要通过containerSelector参数指定<br/>
	 * @param {object} options - 配置
	 * @param {func} containerSelector(string) - 选择器,根据该选择器在scroller中找到后续添加列表元素的根节点,如"ul"对应上文中的节点示例<br/>
	 * @param {func} options.dataRenderer(data) - 根据单条记录渲染单个元素的方法,返回jQuery对象<br/>
	 * @param {func} options.refreshData(render) - 刷新时调用的函数,可以通过ajax或其他方式加载新数据,<br/>
	 * 											并将数据列表送至render中,render方法会清空列表,并将新数据根据dataRenderer参数加载至容器中,<br/>
	 * 											请自行在该方法里面处理没有数据的情形<br/>
	 * @param {func} options.loadMoreData(render) - 加载更多时调用的函数,可以通过ajax或其他方式加载新数据,<br/>
	 * 											并将数据列表送至render中,render方法会将新数据根据dataRenderer参数加入至容器中,<br/>
	 * 											如判断不会再有新数据,请在render前调用noMoreData()方法,容器将暂时禁用"上拉加载"功能,<br/>
	 * 											直至重新刷新数据</br>
	 * @param {func} options.enablePullDown - 是否激活下拉刷新功能,默认为false
	 * @param {func} options.enablePullUp - 是否激活上拉加载功能,默认为false
	 * @param {int} options.triggerOffset - 拉取超过提示框多少时触发"刷新"或"加载"事件,默认为10
	 * @example var scroller = new Scroll(view.$("#scroller")[0], {
	 * 	containerSelector:"ul",
	 * 	refreshData:function(render){
	 * 		App.request("a.do", {success:function(resp){
	 * 			render(resp.data.list);
	 * 		}});
	 * 	},
	 * 	loadMoreData:function(render){
	 * 		App.request("c.do", {success:function(resp){
	 * 			if(resp.data.list.length < 10){
	 * 				scroller.noMoreData();
	 * 			}
	 * 			render(resp.data.list);
	 * 		}});
	 * 	},
	 * 	enablePullDown:true,
	 * 	enablePullUp:true,
	 * 	dataRenderer:function(data){
	 * 		var el = $("<li>" + data.text + "</li>");
	 * 		el.on("tap", function(){
	 * 			console.log(data.text);
	 * 			return false;
	 * 		});
	 * 		return el;
	 * 	}
	 * });
	 * scroller.triggerRefresh();
	 */	
	var Scroll = module.exports = function(el, options){
		
		options = _.extend({}, this._defaultOptions, options);
		// init iscroll for the scroller
		iScroll.call(this, el, options);
		
		this._items = [];
		
		// 滑动时边界检测
		this._threshold = (/iphone|ipad/gi).test(navigator.appVersion) && parseInt(navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/)[1]) > 7;
		
		if(this.options.enablePullDown){
			this._pullDownEl = $(this.PULL_DOWN_HTML).prependTo(this.scroller);
			this.options.topOffset += this._pullDownEl.outerHeight(true);
		}
		
		// 如果有offset,则在此时直接调用iScroll方法调整位置
		if(this.options.topOffset){
			iScroll.prototype._pos.apply(this, [0, -this.options.topOffset]);
		}
		
		if(this.options.containerSelector){
			this._$container = $(this.scroller).find(this.options.containerSelector);
		}else{
			alert("请指定containerSelector参数");
		}
		
		if(this.options.enablePullUp){
			this._pullUpEl = $(this.PULL_UP_HTML).appendTo(this.scroller).hide();
		}
	};
	
	Scroll.prototype = _.extend({}, iScroll.prototype, {
		
		PULL_DOWN_HTML:"<div class='nu-sc-down'><div class='nu-sc-down-icon'>&nbsp;</div><div class='nu-sc-down-text'></div></div>",
		
		PULL_UP_HTML:"<div class='nu-sc-up'><div class='nu-sc-up-icon'>&nbsp;</div><div class='nu-sc-up-text'></div></div>",
		
		STATUS_REFRESH:1,
		STATUS_LOADMORE:2,
		
		_defaultOptions:{
			triggerOffset:20,
			containerSelector:null,
			refreshData:null,
			loadMoreData:null,
			dataRenderer:null,
			enablePullDown:false,
			enablePullUp:false,
			onScrollMove:function(e){
				this._onScrollMove(e);
			},
			onScrollEnd:function(e){
				this._onScrollEnd(e);
			},
			hScrollbar:false,
			vScrollbar:false
		},
		
		_status:null,
		
		_hasMoreDataStatus:false,
		
		_items:null,
		
		threshold:null,
		
		_pullDownEl:null,
		
		_pullUpEl:null,
		
		_noDataTipEl:null,
		
		_onScrollMove:function(e){
			
			if(this._threshold){
				clearTimeout(this._timeout);
				var bcr = this.wrapper.getBoundingClientRect();
				var top = bcr.top;
				var bottom = bcr.bottom;
				
				if((this.pointY <= top || this.pointY >= bottom)){
					// 如果拉动超出边界且需要处理的话,直接触发touchend
					this._onOutOfThreshold(e);
					return;
				}
			}
			
			// 向下拉超出边界
			if(this.options.enablePullDown && this.y > - this.options.topOffset){

				if(this.y > this.options.triggerOffset){
					this._renderReleasePullDown(e);
				}else if(this._status == this.STATUS_REFRESH){
					this._renderPullDown(e);
				}
				return;
			}
			// 向上拉超出边界
			if(this.options.enablePullUp && this.y < Math.min(this.maxScrollY, this.minScrollY) && this._hasMoreDataStatus){
       			// min和max根据容器和内容高度一起加减计算得出
				// 有可能内容不会超过容器高度,所以二者取一

				if(this.y < Math.min(this.maxScrollY, this.minScrollY) - this.options.bottomOffset - this.options.triggerOffset){

					this._renderReleasePullUp(e);
				}else if(this._status == this.STATUS_LOADMORE){
					this._renderPullUp(e);
				}
				return;
			}
		},
		
		_onOutOfThreshold:function(e){
			// 仿照本来的行为,延时执行scrollEnd
			this._resetPos(400);
			var that = this;
			this._timeout = setTimeout(function(){
				that._onScrollEnd(e);
			}, 400);
		},
		
		_onScrollEnd:function(e){
			
			if(this._status == this.STATUS_REFRESH){
				this._renderPullDown(e);
				this._refreshData();
			}else if(this._status == this.STATUS_LOADMORE){
				this._renderPullUp(e);
				this._loadMoreData();
			}
		},
		
		_renderPullDown:function(e){
			this._status = null;
			this._pullDownEl.removeClass("on");
			this._pullDownEl.find(".nu-sc-down-text").html("下拉刷新");
		},
		
		_renderReleasePullDown:function(e){
			this._status = this.STATUS_REFRESH;
			this._pullDownEl.addClass("on");
			this._pullDownEl.find(".nu-sc-down-text").html("松开立即刷新");
		},
		
		_renderPullUp:function(e){
			this._status = null;
			this._pullUpEl.removeClass("on");
			this._pullUpEl.find(".nu-sc-up-text").html("上拉加载更多");
		},
		
		_renderReleasePullUp:function(e){
			this._status = this.STATUS_LOADMORE;
			this._pullUpEl.addClass("on");
			this._pullUpEl.find(".nu-sc-up-text").html("松开立即加载更多");
		},
		
		_refreshData:function(){
			// 如果有上拉记载功能,且当前状态为有更多数据,置为无更多数据状态
			if(this.options.enablePullUp && this._hasMoreDataStatus){
				this._noMoreData();
			}
			this.options.refreshData($.proxy(this._refreshRender, this));
		},
		
		_loadMoreData:function(){
			// 如果有上拉记载功能,且当前状态为有更多数据,置为无更多数据状态
			if(this.options.enablePullUp && this._hasMoreDataStatus){
				this._noMoreData();
			}
			this.options.loadMoreData($.proxy(this._loadMoreRender, this));
		},
		
		_refreshRender:function(datas){
			this._clearItems();
			this._appendItems(datas);
			
			// refresh之后滚动回最顶部,需要把offset计算进去
			iScroll.prototype.scrollTo.apply(this, [0, -this.options.topOffset, 0]);
		},
		
		_loadMoreRender:function(datas){
			this._appendItems(datas);
		},
		
		_clearItems:function(){
			this._$container.empty();
			this._items = [];
		},
		
		_appendItem:function(data){
			var item = this.options.dataRenderer(data);
			this._items.push(item);
			item.appendTo(this._$container);
		},
		
		_appendItems:function(datas){
			for(var i = 0; i < datas.length; i++){
				this._appendItem(datas[i]);
			}
			this._refresh();
		},
		
		_refresh:function(){
			iScroll.prototype.refresh.apply(this, arguments);
		},
		
		_hasMoreData:function(){
			this._hasMoreDataStatus = true;
			
			this._pullUpEl.show();
			this.options.bottomOffset += this._pullUpEl.outerHeight(true);
		},
		
		_noMoreData:function(){
			this._hasMoreDataStatus = false;
			
			this.options.bottomOffset -= this._pullUpEl.outerHeight(true);
			this._pullUpEl.hide();
		},
		
		/**
		 * @desc 触发"下拉刷新"事件,一般用于首次加载数据
		 * @memberof Nuui.Scroll
		 * @func triggerRefresh
		 * @instance
		 */
		triggerRefresh:function(){
			this._refreshData();
		},
		
		/**
		 * @desc 触发"上拉加载"事件
		 * @memberof Nuui.Scroll
		 * @func triggerLoadMore
		 * @instance
		 */
		triggerLoadMore:function(){
			this._loadMoreData();
		},
		
		/**
		 * @desc 通知容器还有新数据,可以通过上拉加载新数据<br/>
		 * 		该方法用于enablePullUp=true时,通知组件展示上拉图标及允许通过上拉触发加载
		 * @memberof Nuui.Scroll
		 * @func hasMoreData
		 * @instance
		 */
		hasMoreData:function(){
			if(this.options.enablePullUp){
				this._hasMoreData();
			}
		}
	});
	
});


